﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using HeresyManager.Comunication;

namespace HeresyManager
{

    [Serializable]
    public class Character
    {
        private List<Tuple<Talent, dynamic>> talents;
        private List<Tuple<Trait, dynamic>> traits;
        private List<Tuple<Skill, dynamic>> skills;
        private List<Gear> gears;
        private List<PsyPower> psy;
        private HomeWorld homeWorld;
        private Career career;
        private List<Rank> ranks = new List<Rank>();
        private List<Disorders> disorders;
        private List<Maligancy> maligancies;
        private List<Faction> factions;
        private Divination divination;

        public int Version { get; internal set; }

        public int AvailableExp { get { return TotalExp - SpendExp; } }
        public int TotalExp { get; set; }
        public int SpendExp { get; set; }
        public int ThronGeld { get; set; }

        public String Name { get; set; }
        public String PlayerName { get; set; }
        public String Description { get; set; }
        public String Quirk { get; set; }
        public int Age { get; set; }
        public bool IsFinished { get; internal set; }

        internal event Func<Character, IEnumerator<Comunication.Comunicator>> CareereChanged;
        public HomeWorld HomeWorld { get { return homeWorld; } }
        public Divination Divination
        {
            get { return divination; }
            set
            {
                if (divination != null) divination.DeAccire(this);
                divination = value;
                if (divination != null) divination.Accire(this);
            }
        }

        public Career Career { get { return career; } }
        public List<Rank> Ranks { get { return ranks; } }
        public List<Faction> Factions
        {
            get { return factions; }
        }

        public ICollection<Tuple<Talent, dynamic>> Talents
        {
            get { return talents; }
        }

        public ICollection<Tuple<Trait, dynamic>> Traits
        {
            get { return traits; }
        }

        public ICollection<Tuple<Skill, dynamic>> Skills
        {
            get { return skills; }
        }

        public ICollection<Gear> Gears
        {
            get { return gears; }
        }

        public ICollection<PsyPower> Psy
        {
            get { return psy; }
        }

        public int WS { get; set; }
        public int BS { get; set; }
        public int S { get; set; }
        public int T { get; set; }
        public int Ag { get; set; }
        public int Int { get; set; }
        public int Per { get; set; }
        public int Wp { get; set; }
        public int Fel { get; set; }

        public int WSBonus { get { return WS / 10; } }
        public int BSBonus { get { return BS / 10; } }
        public int SBonus { get { return S / 10; } }
        public int TBonus { get { return T / 10; } }
        public int AgBonus { get { return Ag / 10; } }
        public int IntBonus { get { return Int / 10; } }
        public int PerBonus { get { return Per / 10; } }
        public int WpBonus { get { return Wp / 10; } }
        public int FelBonus { get { return Fel / 10; } }

        public int TotalWounds { get; set; }
        public int TotalFatepoints { get; set; }

        public int InsanityPoints { get; set; }
        public int DegreeOfMadness { get; set; }
        public List<Disorders> Disorders
        {
            get { return disorders; }
        }

        public int CorruptionPoints { get; set; }
        public int DegreeOfCorruption { get; set; }
        public List<Maligancy> Maligancies
        {
            get { return maligancies; }
        }

        public Gender Gender { get; set; }

        public int MovementHalfAction { get; set; }
        public int MovementFullAction { get; set; }
        public int MovementCharge { get; set; }
        public int MovementRun { get; set; }

        public int ArmorHead { get; set; }
        public String ArmorTypeHead { get; set; }
        public int ArmorRightArm { get; set; }
        public String ArmorTypeRightArm { get; set; }
        public int ArmorLeftArm { get; set; }
        public String ArmorTypeLeftArm { get; set; }
        public int ArmorBody { get; set; }
        public String ArmorTypeBody { get; set; }
        public int ArmorRightLeg { get; set; }
        public String ArmorTypeRightLeg { get; set; }
        public int ArmorLeftLeg { get; set; }
        public String ArmorTypeLeftLeg { get; set; }



        public int TimesSkilledWS { get; set; }
        public int TimesSkilledBS { get; set; }
        public int TimesSkilledS { get; set; }
        public int TimesSkilledT { get; set; }
        public int TimesSkilledAg { get; set; }
        public int TimesSkilledInt { get; set; }
        public int TimesSkilledPer { get; set; }
        public int TimesSkilledWp { get; set; }
        public int TimesSkilledFel { get; set; }


        [ContractInvariantMethod]
        void Invariants()
        {
            Contract.Invariant(Traits != null);
            Contract.Invariant(Skills != null);
            Contract.Invariant(Gears != null);
            Contract.Invariant(Psy != null);
            Contract.Invariant(Disorders != null);
            Contract.Invariant(Maligancies != null);
            Contract.Invariant(Talents != null);
        }

        public Character()
        {
            traits = new List<Tuple<Trait, dynamic>>();
            skills = new List<Tuple<Skill, dynamic>>();
            gears = new List<Gear>();
            psy = new List<PsyPower>();
            disorders = new List<Disorders>();
            maligancies = new List<Maligancy>();
            talents = new List<Tuple<Talent, dynamic>>();
        }

        public IEnumerator<Comunicator> AddSkill(Skill skill, dynamic source)
        {
            skills.Add(new Tuple<Skill, dynamic>(skill, source));
            return skill.Accire(this);
        }

        public bool RemoveSkill(Skill skill, dynamic source)
        {
            return skills.Remove(new Tuple<Skill, dynamic>(skill, source));
        }

        public IEnumerator<Comunicator> AddTrait(Trait trait, dynamic source)
        {
            traits.Add(new Tuple<Trait, dynamic>(trait, source));
            return trait.Accire(this);
        }

        public bool RemoveTrait(Trait trait, dynamic source)
        {
            var value = traits.Remove(new Tuple<Trait, dynamic>(trait, source));
            trait.DeAccire(this);
            return value;
        }

        public IEnumerator<Comunicator> AddMaligancy(Maligancy maligancy)
        {
            maligancies.Add(maligancy);
            return maligancy.Accire(this);
        }

        public void RemoveMaligancy(Maligancy maligancy)
        {
            var value = maligancies.Remove(maligancy);
            maligancy.DeAccire(this);
        }


        public IEnumerator<Comunicator> AddTalent(Talent talent, dynamic source)
        {
            talents.Add(new Tuple<Talent, dynamic>(talent, source));
            return talent.Accire(this);
        }

        public bool RemoveTalent(Talent talent, dynamic source)
        {
            var value = talents.Remove(new Tuple<Talent, dynamic>(talent, source));
            talent.DeAccire(this);
            return value;
        }

        public IEnumerator<Comunicator> SetHomeWorld(Type homeworld)
        {
            Contract.Requires(homeworld.IsSubclassOf(typeof(HomeWorld)));

            var cont = homeworld.GetConstructor(new Type[0]);
            var newHomeworld = cont.Invoke(new object[0]) as HomeWorld;
            if (this.homeWorld != null)
                this.homeWorld.DeAcire(this);
            this.homeWorld = newHomeworld;
            return this.homeWorld.Acire(this);
        }




        public IEnumerator<Comunicator> SetCareer(Type career)
        {
            Contract.Requires(career.IsSubclassOf(typeof(Career)));

            var cont = career.GetConstructor(new Type[0]);
            var newCareere = cont.Invoke(new object[0]) as Career;
            if (this.career != null)
                this.career.DeAcire(this);
            this.career = newCareere;
            var e = this.career.Acire(this);
            while (e.MoveNext())
                yield return e.Current;
            if (CareereChanged != null)
                e = this.CareereChanged(this);
            while (e.MoveNext())
                yield return e.Current;
        }

        public void AddExp(int exp)
        {
            if (exp < 0)
                throw new ArgumentException("exp must be Positiv");
            this.TotalExp += exp;
        }

        public IEnumerator<Comunicator> GetNewRank()
        {
            var count = Career.Ranks.Count(t => t.IsPreconditionSatisfied(this) && !this.Ranks.Contains(t));
            if (count == 1)
            {
                Ranks.Add(Career.Ranks.Single(t => t.IsPreconditionSatisfied(this) && !this.Ranks.Contains(t)));
            }
            else if (count > 1)
            {
                var ask = new Comunication.ChooseOne<Rank>("Rank", (from r in Career.Ranks where r.IsPreconditionSatisfied(this) && !this.Ranks.Contains(r) select r).ToArray());
                while (ask.SelectedValue == null)
                    yield return ask;
                Ranks.Add(ask.SelectedValue);
            }
        }

        public bool CanAccireNewRank { get { return Career == null ? false : Career.Ranks.Count(t => t.IsPreconditionSatisfied(this) && !this.Ranks.Contains(t)) > 0; } }


        public bool CanAccireWS { get { return Career == null ? false : Career.WeaponSkillCost.Length > TimesSkilledWS && Career.WeaponSkillCost[TimesSkilledWS] <= AvailableExp; } }

        public bool CanAccireBS { get { return Career == null ? false : Career.BalisticSkillCost.Length > TimesSkilledBS && Career.BalisticSkillCost[TimesSkilledBS] <= AvailableExp; } }

        public bool CanAccireS { get { return Career == null ? false : Career.StrengthCost.Length > TimesSkilledS && Career.StrengthCost[TimesSkilledS] <= AvailableExp; } }

        public bool CanAccireT { get { return Career == null ? false : Career.ToughnessCost.Length > TimesSkilledT && Career.ToughnessCost[TimesSkilledT] <= AvailableExp; } }

        public bool CanAccireAg { get { return Career == null ? false : Career.AgilityCost.Length > TimesSkilledAg && Career.AgilityCost[TimesSkilledAg] <= AvailableExp; } }

        public bool CanAccireInt { get { return Career == null ? false : Career.IntelligenceCost.Length > TimesSkilledInt && Career.IntelligenceCost[TimesSkilledInt] <= AvailableExp; } }

        public bool CanAccirePer { get { return Career == null ? false : Career.PerceptionCost.Length > TimesSkilledPer && Career.PerceptionCost[TimesSkilledPer] <= AvailableExp; } }

        public bool CanAccireWp { get { return Career == null ? false : Career.WillpowerCost.Length > TimesSkilledWp && Career.WillpowerCost[TimesSkilledWp] <= AvailableExp; } }

        public bool CanAccireFel { get { return Career == null ? false : Career.FellowshipCost.Length > TimesSkilledFel && Career.FellowshipCost[TimesSkilledFel] <= AvailableExp; } }


        public void AccireWS()
        {
            this.SpendExp += this.Career.WeaponSkillCost[this.TimesSkilledWS];
            TimesSkilledWS++;
            WS += 5;
        }
        public void AccireBS()
        {
            this.SpendExp += this.Career.BalisticSkillCost[this.TimesSkilledBS];
            TimesSkilledBS++;
            BS += 5;
        }
        public void AccireS()
        {
            this.SpendExp += this.Career.StrengthCost[this.TimesSkilledS];
            TimesSkilledS++;
            S += 5;
        }
        public void AccireT()
        {
            this.SpendExp += this.Career.ToughnessCost[this.TimesSkilledT];
            TimesSkilledT++;
            T += 5;
        }
        public void AccireAg()
        {
            this.SpendExp += this.Career.AgilityCost[this.TimesSkilledAg];
            TimesSkilledAg++;
            Ag += 5;
        }
        public void AccireInt()
        {
            this.SpendExp += this.Career.IntelligenceCost[this.TimesSkilledInt];
            TimesSkilledInt++;
            Int += 5;
        }
        public void AccirePer()
        {
            this.SpendExp += this.Career.PerceptionCost[this.TimesSkilledPer];
            TimesSkilledPer++;
            Per += 5;
        }
        public void AccireWp()
        {
            this.SpendExp += this.Career.WillpowerCost[this.TimesSkilledWp];
            TimesSkilledWp++;
            Wp += 5;
        }
        public void AccireFel()
        {
            this.SpendExp += this.Career.FellowshipCost[this.TimesSkilledFel];
            TimesSkilledFel++;
            Fel += 5;
        }
    }
    [Serializable]
    public enum Gender
    {
        Undefined,
        Male,
        Female
    }

    public interface IMinMaxRole
    {
        int MinRole { get; }
        int MaxRole { get; }
    }

    [Serializable]
    public class Build : IMinMaxRole
    {
        private int minrol;
        private int maxrol;
        private float size;
        private float weight;
        private Gender gender;
        private String descripton;

        public Build()
        {
        }

        public int MinRole
        {
            get { return minrol; }
            set { minrol = value; }
        }
        public int MaxRole
        {
            get { return maxrol; }
            set { maxrol = value; }
        }
        public float Size
        {
            get { return size; }
            set { size = value; }
        }
        public float Weight
        {
            get { return weight; }
            set { weight = value; }
        }
        public Gender Gender
        {
            get { return gender; }
            set { gender = value; }
        }
        public String Descripton
        {
            get { return descripton; }
            set { descripton = value; }
        }

        public override string ToString()
        {
            return Descripton;
        }
    }
    [Serializable]
    public class Age : IMinMaxRole
    {
        private int minRol;
        private int maxRol;
        private IDice dice;
        private String description;

        public Age()
        {
        }

        public int MinRole
        {
            get { return minRol; }
            set { minRol = value; }
        }
        public int MaxRole
        {
            get { return maxRol; }
            set { maxRol = value; }
        }
        public IDice Dice
        {
            get { return dice; }
            set { dice = value; }
        }
        public String Description
        {
            get { return description; }
            set { description = value; }
        }

        public override string ToString()
        {
            return Description;
        }
    }

    [Serializable]
    public class Color : IMinMaxRole
    {
        private int minRol;
        private int maxRol;
        private String name;

        public Color()
        {
        }

        public int MinRole
        {
            get { return minRol; }
            set { minRol = value; }
        }
        public int MaxRole
        {
            get { return maxRol; }
            set { maxRol = value; }
        }
        public String Name
        {
            get { return name; }
            set { name = value; }
        }

        public override string ToString()
        {
            return Name;
        }
    }

    public class Quirk : IMinMaxRole
    {
        private int minRol;
        private int maxRol;
        private String description;

        public Quirk()
        {
        }

        public int MinRole
        {
            get { return minRol; }
            set { minRol = value; }
        }
        public int MaxRole
        {
            get { return maxRol; }
            set { maxRol = value; }
        }
        public String Description
        {
            get { return description; }
            set { description = value; }
        }

        public override string ToString()
        {
            return Description;
        }
    }

    public class Origin : IMinMaxRole
    {
        private int minRole;
        private int maxRole;
        private String description;

        public Origin()
        {
        }

        public int MinRole
        {
            get { return minRole; }
            set { minRole = value; }
        }
        public int MaxRole
        {
            get { return maxRole; }
            set { maxRole = value; }
        }
        public String Description
        {
            get { return description; }
            set { description = value; }
        }

        public override string ToString()
        {
            return Description;
        }
    }
}
